package com.infolab.aims.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.infolab.aims.common.InQueryHelper;
import com.infolab.aims.common.PaginationHelper;
import com.infolab.aims.common.QueryHelper;
import com.infolab.aims.common.bean.SearchCriteria;
import com.infolab.aims.common.bean.SearchResult;
import com.infolab.aims.domain.Location;
import com.infolab.aims.domain.Section;
import com.infolab.aims.domain.asset.AssetModel;
import com.infolab.aims.service.intf.LocationService;


@Service
@Transactional
public class LocationServiceImpl implements LocationService {
	
	Log logger = LogFactory.getLog(getClass());
	@PersistenceContext
	private EntityManager em;
	
	public void createLocation(Location location)
	{
		location.setActive(true);
		
		Location newLocation = new Location();
		
		location.setModifiedBy(location.getCreatedBy());
		location.setDateCreated(new Date());
		location.setDateModified(new Date());
		
		BeanUtils.copyProperties(location,newLocation);
		
		em.persist(newLocation);
		
		location.setLocationId(newLocation.getLocationId());

	}

	public void updateLocation(Location location) {
		
		if(location.getSection()==null || location.getSection().getSectionId()==null)
			location.setSection(null);
		
		Location newLocation = em.find(Location.class, location.getLocationId());
		
		location.setDateModified(new Date());
		BeanUtils.copyProperties(location,newLocation);
		
		em.merge(newLocation);
	}


	public Location getLocation(Integer locationId) {
		
		return em.find(Location.class, locationId);
	}

	public Location findLocation(SearchCriteria searchCriteria) {
		searchCriteria.setMaxRow(1);
		List<Location> locations  = findLocations(searchCriteria);
		if(locations!=null && locations.size()>=1)
			return locations.get(0);
		
		return null;
	}
	
	public List<Location> findLocations(SearchCriteria searchCriteria) {
		if(searchCriteria==null) searchCriteria = new SearchCriteria();
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<Location> cq = cb.createQuery(Location.class);
		Root<Location> root = cq.from(Location.class);
		List<Predicate> criteria = new ArrayList<Predicate>();
		
		if (searchCriteria.getSearchCriteria("locationCode")!= null) {
			ParameterExpression<String> p = cb.parameter(String.class, "locationCode");
			criteria.add(cb.equal(cb.upper(root.<String>get("locationCode")), cb.upper( p)));
		}
		
		cq.select(root);
		if (criteria.size() == 0) {
			} else if (criteria.size() == 1) {
			cq.where(criteria.get(0));
			} else {
			cq.where(cb.and(criteria.toArray(new Predicate[0])));
		}
		
		if(StringUtils.isEmpty(searchCriteria.getSortName()))
		{
			cq.orderBy(cb.asc(root.get("locationName")));
		}
		
		Query query = em.createQuery(cq);

		if (searchCriteria.getSearchCriteria("locationCode")!= null) {
			query.setParameter("locationCode", searchCriteria.getSearchCriteria("locationCode"));
		}
		if(searchCriteria.getMaxRow()!=null)
			query.setMaxResults(searchCriteria.getMaxRow());
		
		
		return query.getResultList();
	}
	
	public List<Location> searchLocationCriteria(SearchCriteria searchCriteria) {

		if(searchCriteria==null) searchCriteria = new SearchCriteria();
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<Location> cq = cb.createQuery(Location.class);
		
		Root<Location> root = cq.from(Location.class);
		List<Predicate> criteria = new ArrayList<Predicate>();
		
		Join<Location,Section> section = null;
		
		if (searchCriteria.getSearchCriteria("sectionId")!= null ) {
			section = root.join("section", JoinType.INNER);
			ParameterExpression<Integer> p = cb.parameter(Integer.class, "sectionId");
			criteria.add(cb.equal(section.get("sectionId"),  p));
		}
		
		if (searchCriteria.getSearchCriteria("active")!= null) {
			ParameterExpression<Boolean> p = cb.parameter(Boolean.class, "active");
			criteria.add(cb.equal(root.get("active"),  p));
		}

		if (searchCriteria.getSearchCriteria("locationCode")!= null) {
			
			Object obj = searchCriteria.getSearchCriteria("locationCode");
			if(obj instanceof InQueryHelper)
			{	
				ParameterExpression<List> p = cb.parameter(List.class, "locationCode");
				criteria.add(cb.in(root.get("locationCode")).value(p));
			}
		}
		
		cq.select(root);
		
		if (criteria.size() == 0) {
			} else if (criteria.size() == 1) {
			cq.where(criteria.get(0));
			} else {
			cq.where(cb.and(criteria.toArray(new Predicate[0])));
		}
		
		Query query = em.createQuery(cq);
		
		if (searchCriteria.getSearchCriteria("sectionId")!= null) {
			Integer tmp = (Integer) searchCriteria.getSearchCriteria("sectionId");
			query.setParameter("sectionId", tmp);
		}
		
		if (searchCriteria.getSearchCriteria("active")!= null) {
			boolean tmp = (Boolean) searchCriteria.getSearchCriteria("active");
			query.setParameter("active", tmp);
		}
		
		if (searchCriteria.getSearchCriteria("locationCode")!= null) {
			Object obj = searchCriteria.getSearchCriteria("locationCode");
			if(obj instanceof InQueryHelper)
			{
				query.setParameter("locationCode", ((InQueryHelper)obj).getColumnValues());
			}
		}
		
		if(searchCriteria.getMaxRow()!=null)
			query.setMaxResults(searchCriteria.getMaxRow());
		
		return query.getResultList();
	
	}

	public SearchResult searchLocation(
			SearchCriteria searchCriteria) {
		
		if(searchCriteria.getCurPage()==null)
			searchCriteria.setCurPage(1);
		
		LocationPaginationHelper helper = new LocationPaginationHelper();
		
		return helper.getSearchResultFromQueryHelper(em, searchCriteria);
	}
	
	public void deleteLocation(Location location) {
		
		location.setDateModified(new Date());
		
		location.setActive(false);
		
		em.merge(location);
	}
	
	public static class LocationPaginationHelper extends PaginationHelper<Location> {

		@Override
		public String[] getValidColumnName() {
			return new String[]{"locationCode","locationName","active"};
		}
		
		@Override
		public String[] getValidSortName() {
			return new String[]{"locationCode","locationName","section.sectionName","modifiedBy","dateModified"};
		}

		@Override
		public String getDefaultSortName() {
			return "locationName";
		}
		
		@Override
		public QueryHelper getQueryHelper() {
			QueryHelper queryHelper = new QueryHelper();
			queryHelper.select("select location from Location location");
			return queryHelper;
		}
	}
}
